Skip to content

[中文版本](/posts/c教程/zh-CN/08-调试测试与 profiling/071-Debugging with gdb)

Every C programmer eventually meets a segmentation fault, and that’s when you discover your most powerful companion: gdb, the GNU Debugger. Debugging isn’t about luck; it’s about learning to inspect a program as it runs, to pause time, and to see what the computer is really doing.

Let’s learn how to use gdb to find bugs, inspect memory, trace crashes, and truly understand your code.

Step 1. Compiling with Debug Information ​

Before you can debug, you need to tell the compiler to include symbol information (variable names, line numbers, etc.). Use the-g flag:

gcc -g main.c -o main

You can now open it in gdb:

gdb ./main

Step 2. Starting and Running Your Program ​

Inside gdb, you can run your program just like normal:

(gdb) run

If it crashes, you’ll get something like:

Program received signal SIGSEGV, Segmentation fault.
0x000000000040114a in buggy_function () at main.c:12
12        *ptr = 42;

You now know exactly where it failed.

Step 3. Setting Breakpoints ​

A breakpoint tells gdb to pause before executing a specific line or function.

(gdb) break main
Breakpoint 1 at 0x40113b: file main.c, line 5.
(gdb) run

When the program stops, you can inspect state:

(gdb) print variable_name
(gdb) next
(gdb) step
  • next executes the next line (skipping function calls).
  • step goes into a function call.

Step 4. Example: A Simple Bug ​

Here’s a program with a classic segmentation fault.

#include <stdio.h>
void buggy(void) {
    int *p = NULL;
    *p = 10;
}
int main(void) {
    printf("Before crash\n");
    buggy();
    printf("After crash\n");
}

Compile and debug:

gcc -g bug.c -o bug
gdb ./bug
(gdb) run

Output:

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555159 in buggy () at bug.c:5
5        *p = 10;

Now inspect:

(gdb) backtrace
#0 buggy () at bug.c:5
#1 main () at bug.c:10

You’ve just traced the crash from main to the exact faulty line.

Step 5. Inspecting Variables ​

You can view variable values anytime:

(gdb) print x
(gdb) print *ptr

You can also modify them:

(gdb) set variable x = 100

To list all local variables:

(gdb) info locals

Step 6. Navigating Execution ​

Common gdb commands:

CommandAction
runStart the program
break NStop at line N
nextRun next line
stepStep into function
continueResume until next breakpoint
finishRun until function returns
backtraceShow call stack
info localsList local vars
print VARShow value
quitExit gdb

Step 7. Watching Variables ​

You can set watchpoints, gdb stops when a variable changes.

int counter = 0;
for (int i = 0; i < 10; i++)
    counter += i;

In gdb:

(gdb) watch counter
(gdb) run

Every timecounter changes, the program pauses, showing where it happened.

Step 8. Conditional Breakpoints ​

Sometimes you only want to stop under specific conditions:

(gdb) break loop.c:25 if i > 100

This breakpoint triggers only wheni exceeds 100.

Step 9. Inspecting Memory and Registers ​

You can inspect raw memory:

(gdb) x/10x &array

This prints 10 hexadecimal words starting at&array.

Or view registers:

(gdb) info registers

This is useful for low-level debugging (e.g., compilers, OS kernels, embedded code).

Step 10. Tiny Code: Debugging a Logic Bug ​

#include <stdio.h>
int main(void) {
    int sum = 0;
    for (int i = 1; i <= 5; i++) {
        sum = sum + i;
    }
    printf("Sum = %d\n", sum); // should be 15
}

Introduce a bug:

for (int i = 1; i <= 5; i++); // extra semicolon!

Compile and debug:

gcc -g bug.c -o bug
gdb ./bug
(gdb) break main
(gdb) run
(gdb) print sum
(gdb) next
(gdb) print sum

You’ll see thatsum never changes, because the loop body was empty.

Why It Matters ​

Debugging is how you learn to think like the machine:

  • You can inspect what happens between lines.
  • You can see every variable’s value.
  • You can find segmentation faults in seconds.

Learning gdb teaches you how C really runs, from stack frames to pointers.

Try It Yourself ​

  1. Write a program that crashes (e.g., use a null pointer) and trace the cause with gdb.
  2. Usenext andstep to trace a recursive function.
  3. Set a watchpoint on a variable in a loop.
  4. Add a conditional breakpoint that triggers only when a value exceeds a limit.
  5. Explorebacktrace andinfo locals after a crash.

Next, you’ll learn how to detect hidden memory errors, leaks, invalid frees, and buffer overflows, using the indispensable Valgrind tool.

本站总访问 — 次